<template>
	<div class="upload-container">
		<!-- 上传区域 -->
		<div class="upload-area" :class="{ 'is-disabled': props.disabled }">
			<el-upload
				ref="uploadRef"
				action="/api/_file/Upload"
				:on-success="onSuccess"
				:on-remove="onRemove"
				:on-preview="onDownload"
				:on-error="onError"
				:before-upload="beforeUpload"
				:headers="header"
				:limit="props.limit"
				v-model:file-list="files"
				:disabled="props.disabled"
				:multiple="props.multi"
				:show-file-list="true"
				:accept="acceptTypes"
				class="upload-component"
			>
				<el-button type="primary" v-if="!props.disabled">
					<el-icon class="upload-icon">
						<Upload />
					</el-icon>
					{{ $t('message._system.uploadFile.select') }}
				</el-button>
				<template #file="{ file }">
					<div
						class="file-item"
						:class="{
							'file-item--uploading': file.status === 'uploading',
							'file-item--success': file.status === 'success',
						}"
					>
						<el-icon class="file-icon" :class="{ rotating: file.status === 'uploading' }">
							<Upload v-if="file.status === 'uploading'" />
							<Document v-else />
						</el-icon>
						<div class="file-content">
							<el-tooltip :content="file.name" placement="top" :disabled="file.name.length < 30">
								<span class="file-name" @click.stop="onDownload(file)">{{ file.name }}</span>
							</el-tooltip>
							<span class="file-size" v-if="file.size">{{ formatBytes(file.size) }}</span>
							<span class="upload-status" v-if="file.status === 'uploading'">上传中...</span>
						</div>
						<div class="file-actions" v-if="!props.disabled">
							<el-button link type="danger" size="small" @click.stop="onRemove(file)">
								<el-icon><Close /></el-icon>
							</el-button>
						</div>
					</div>
				</template>
				<template #tip v-if="!props.disabled">
					<div class="upload-tip">
						<div v-if="props.multi">
							<small class="text-muted">{{ $t('message._system.uploadFile.total', { count: files?.length }) }} / {{ props.limit }}</small>
						</div>
						<div class="file-type-tip" v-if="fileTypeTip">
							<small>{{ fileTypeTip }}</small>
						</div>
					</div>
				</template>
			</el-upload>
		</div>
	</div>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import type { PropType } from 'vue';
import { Local } from '/@/utils/storage';
import fileApi from '/@/api/file';
import { ElMessage } from 'element-plus';
import { Upload, Document, Close } from '@element-plus/icons-vue';

const props = defineProps({
	multi: Boolean,
	disabled: Boolean,
	modelValue: null,
	deletedFiles: null,
	maxSize: { type: Number, default: 50 },
	allowedTypes: { type: Array as PropType<string[]>, default: () => ['*'] },
	limit: { type: Number, default: 10 },
});

const emit = defineEmits(['update:modelValue', 'update:deletedFiles', 'upload-status']);

const files = ref<any[]>([]);
const uploadRef = ref();
const isUpdatingFromUpload = ref(false);
// 文件名缓存与去重请求缓存，避免多次重复调用接口
const fileNameCache = new Map<string, string>();
const fileNamePromiseCache = new Map<string, Promise<string>>();

const fileValue = computed({
	get: () => props.modelValue,
	set: (value) => emit('update:modelValue', value),
});

const deletedFiles = computed({
	get: () => props.deletedFiles ?? [],
	set: (value) => emit('update:deletedFiles', value),
});

const header = computed(() => ({ Authorization: `Bearer ${Local.get('token')}` }));

const isUploading = computed(() => files.value.some((file) => ['uploading', 'loading'].includes(file.status)));

// 生成accept属性字符串
const acceptTypes = computed(() => {
	if (!props.allowedTypes || props.allowedTypes.includes('*')) {
		return '';
	}
	return props.allowedTypes.join(',');
});

// MIME类型到友好名称的映射
const mimeTypeMap: Record<string, string> = {
	'image/jpeg': 'JPEG图片',
	'image/jpg': 'JPG图片',
	'image/png': 'PNG图片',
	'image/gif': 'GIF图片',
	'image/webp': 'WebP图片',
	'image/svg+xml': 'SVG图片',
	'application/pdf': 'PDF文档',
	'application/msword': 'Word文档',
	'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'Word文档',
	'application/vnd.ms-excel': 'Excel表格',
	'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'Excel表格',
	'application/vnd.ms-powerpoint': 'PowerPoint演示文稿',
	'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'PowerPoint演示文稿',
	'text/plain': '文本文件',
	'text/csv': 'CSV文件',
	'application/zip': 'ZIP压缩包',
	'application/x-rar-compressed': 'RAR压缩包',
	'application/x-7z-compressed': '7Z压缩包',
	'video/mp4': 'MP4视频',
	'video/avi': 'AVI视频',
	'video/mov': 'MOV视频',
	'audio/mp3': 'MP3音频',
	'audio/wav': 'WAV音频',
};

// 文件类型提示
const fileTypeTip = computed(() => {
	if (!props.allowedTypes || props.allowedTypes.includes('*')) {
		return '';
	}

	const typeNames = props.allowedTypes.map((type) => mimeTypeMap[type] || type).join('、');
	return `支持的文件类型：${typeNames}`;
});

watch(isUploading, (status) => emit('upload-status', status), { immediate: true });

const beforeUpload = (file: File) => {
	const sizeValid = file.size / 1024 / 1024 < props.maxSize;
	const typeValid = props.allowedTypes[0] === '*' || props.allowedTypes.includes(file.type);
	const countValid = files.value.length < props.limit;

	if (!sizeValid) {
		ElMessage.error(`文件大小不能超过 ${props.maxSize}MB!`);
		return false;
	}
	if (!typeValid) {
		const allowedTypesText = props.allowedTypes.map((type) => mimeTypeMap[type] || type).join('、');
		ElMessage.error(`不支持的文件类型！支持的类型：${allowedTypesText}`);
		return false;
	}
	if (!countValid) {
		ElMessage.warning(`最多只能上传 ${props.limit} 个文件`);
		return false;
	}
	return true;
};

const onError = (error: any) => {
	ElMessage.error('文件上传失败，请重试');
	console.error('Upload error:', error);
};

const clearFiles = () => files.value.forEach(onRemove);

// 统一从各种形态中提取文件ID：
// - 字符串/数字
// - { FileId: string | { Id } }
// - { ID | Id | id }
const extractFileId = (source: any): string | null => {
	if (!source) return null;
	if (Array.isArray(source)) return extractFileId(source[0]);
	if (typeof source === 'string' || typeof source === 'number') return String(source);
	if (typeof source === 'object') {
		if (source.FileId) return extractFileId(source.FileId);
		const id = source.ID ?? source.Id ?? source.id;
		if (id) return String(id);
	}
	return null;
};

const createFileObject = async (fileId: string, item: any, index: number) => {
	try {
		// 优先使用已有的文件名，减少一次网络请求
		let filename = item?.FileName as string | undefined;
		if (!filename) {
			// 先读缓存
			if (fileNameCache.has(fileId)) {
				filename = fileNameCache.get(fileId) as string;
			} else {
				// 去重并缓存同一ID的并发请求
				let p = fileNamePromiseCache.get(fileId);
				if (!p) {
					p = fileApi()
						.getName(fileId)
						.then((res: any) => (typeof res === 'string' ? res : ''))
						.finally(() => fileNamePromiseCache.delete(fileId));
					fileNamePromiseCache.set(fileId, p);
				}
				filename = await p;
				if (filename) fileNameCache.set(fileId, filename);
			}
		}
		return {
			name: filename,
			url: `/api/_file/getfile/${fileId}`,
			FileId: fileId,
			keyID: item.ID,
			Order: index,
			downloading: false,
			status: 'success',
		};
	} catch (error) {
		console.error('获取文件名失败:', error);
		return {
			name: item?.FileName || '未知文件',
			url: '',
			FileId: fileId,
			keyID: item.ID,
			Order: index,
			downloading: false,
			status: 'error',
			hasError: true,
		};
	}
};

watch(
	fileValue,
	async () => {
		if (isUpdatingFromUpload.value || !fileValue.value) {
			if (!fileValue.value) files.value = [];
			return;
		}

		try {
			if (!props.multi) {
				const currentFileId = files.value[0]?.FileId;
				const id = extractFileId(props.modelValue);
				if (id !== currentFileId) {
					files.value = id ? [await createFileObject(id, props.modelValue, 0)] : [];
				}
			} else {
				if (!Array.isArray(props.modelValue) || props.modelValue.length === 0) {
					files.value = [];
					return;
				}

				const newFiles: any[] = [];
				for (let i = 0; i < props.modelValue.length; i++) {
					const item = props.modelValue[i];
					const fileId = extractFileId(item);
					if (!fileId) continue;

					const existingFile = files.value.find((f) => f.FileId === fileId);
					newFiles.push(existingFile || (await createFileObject(fileId, item, i)));
				}
				files.value = newFiles;
			}
		} catch (error) {
			ElMessage.error('获取文件信息失败');
			console.error('Error fetching file info:', error);
		}
	},
	{ immediate: true }
);

const onDownload = async (file: any) => {
	try {
		file.downloading = true;
		await fileApi().downloadFile(file.FileId);
		ElMessage.success('下载成功');
	} catch (error) {
		ElMessage.error('下载失败');
		console.error('Download error:', error);
	} finally {
		file.downloading = false;
	}
};

const formatBytes = (bytes: number) => {
	if (!bytes) return '';
	const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
	const i = Math.floor(Math.log(bytes) / Math.log(1024));
	return `${(bytes / Math.pow(1024, i)).toFixed(1)}${sizes[i]}`;
};

const updateModelValue = (validFiles: any[]) => {
	isUpdatingFromUpload.value = true;
	// 输出与图片上传组件一致：多文件为 { FileId, Order }[]，单文件为 string|null
	fileValue.value = props.multi ? validFiles.map((item, i) => ({ FileId: item.FileId, Order: i })) : validFiles[0]?.FileId || null;
	setTimeout(() => {
		isUpdatingFromUpload.value = false;
	}, 100);
};

const onSuccess = (res: any, uploadFile: any) => {
	uploadFile.FileId = res.Id;
	uploadFile.status = 'success';
	ElMessage.success('上传成功');

	const newFile = {
		name: uploadFile.name,
		url: `/api/_file/getfile/${res.Id}`,
		FileId: res.Id,
		downloading: false,
		status: 'success',
		uid: uploadFile.uid,
	};

	if (!props.multi) {
		if (files.value.length > 0) {
			deletedFiles.value.push(files.value[0].FileId);
		}
		files.value = [newFile];
		fileValue.value = res.Id;
	} else {
		const uploadingIndex = files.value.findIndex((f) => f.uid === uploadFile.uid);
		const existingIndex = files.value.findIndex((f) => f.FileId === res.Id);

		if (uploadingIndex !== -1) {
			files.value[uploadingIndex] = newFile;
		} else if (existingIndex === -1) {
			files.value.push(newFile);
		} else {
			files.value[existingIndex] = newFile;
		}

		const validFiles = files.value.filter((f) => f.FileId && f.status === 'success');
		updateModelValue(validFiles);
	}
};

const onRemove = (file: any) => {
	if (file.FileId && file.status !== 'uploading') {
		fileApi().deleteFile(file.FileId);
		deletedFiles.value.push(file.FileId);
	}

	const index = files.value.findIndex((item) => (item.FileId ? item.FileId === file.FileId : item.uid === file.uid));

	if (index > -1) {
		files.value.splice(index, 1);

		if (!props.multi) {
			fileValue.value = null;
		} else {
			const validFiles = files.value.filter((f) => f.FileId && f.status === 'success');
			updateModelValue(validFiles);
		}
		ElMessage.success('文件已删除');
	}
};

defineExpose({
	clearFiles,
});
</script>

<style scoped lang="scss">
.upload-container {
	width: 100%;

	.upload-area {
		padding: 8px;
		text-align: left;

		.upload-icon {
			margin-right: 4px;
		}
	}

	.upload-tip {
		margin-top: 4px;
		color: var(--el-text-color-secondary);
		font-size: 12px;
		display: block;
		margin-bottom: 8px;
		clear: both;
		position: relative;
		z-index: 1;

		.file-type-tip {
			color: var(--el-color-danger);
			margin-top: 2px;
		}
	}
}

:deep(.el-upload-list) {
	margin-top: 8px;
	clear: both;
	display: block;
}

// 只读模式下（没有上传按钮）让文件与标签同行
.upload-area.is-disabled :deep(.el-upload-list) {
	margin-top: -40px;
}

:deep(.el-upload) {
	width: auto;
	display: inline-block;
	vertical-align: top;
}

:deep(.el-upload-list__item) {
	transition: none;

	&:hover {
		background-color: transparent;
	}

	.el-upload-list__item-status-label {
		display: none;
	}

	.el-icon--close {
		display: none;
	}
}

.file-item {
	display: inline-flex;
	align-items: center;
	padding: 6px 12px;
	margin: 4px 8px 4px 0;
	background: var(--el-fill-color-lighter);
	border: 1px solid var(--el-border-color-light);
	border-radius: 4px;
	gap: 6px;
	line-height: 1.4;
	transition: all 0.3s ease;
	position: relative;

	&:hover {
		background: var(--el-fill-color-light);
		border-color: var(--el-color-primary);
	}

	&.file-item--uploading {
		border-color: var(--el-color-primary-light-5);
		background: var(--el-color-primary-light-9);

		&::after {
			content: '';
			position: absolute;
			top: 0;
			left: 0;
			right: 0;
			height: 2px;
			background: linear-gradient(90deg, var(--el-color-primary), var(--el-color-primary-light-3));
			border-radius: 4px 4px 0 0;
			animation: uploadPulse 1.5s ease-in-out infinite;
		}
	}

	&.file-item--success {
		border-color: var(--el-color-success);
		background: var(--el-fill-color-lighter);
		animation: successBounce 0.6s ease-out;
	}
}

.file-icon {
	font-size: 14px;
	color: var(--el-color-primary);
	flex-shrink: 0;
	transition: all 0.3s ease;

	&.rotating {
		animation: rotate 1s linear infinite;
	}
}

.file-content {
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;

	.file-name {
		font-size: 13px;
		color: var(--el-text-color-primary);
		cursor: pointer;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		text-decoration: none;
		font-weight: 500;

		&:hover {
			color: var(--el-color-primary);
		}
	}

	.file-size {
		font-size: 11px;
		color: var(--el-text-color-secondary);
		flex-shrink: 0;
		background: var(--el-fill-color);
		padding: 1px 4px;
		border-radius: 2px;
	}

	.upload-status {
		font-size: 11px;
		color: var(--el-color-primary);
		flex-shrink: 0;
		font-weight: 500;
	}
}

.file-actions {
	flex-shrink: 0;
	margin-left: 2px;

	.el-button {
		padding: 2px;
		opacity: 0.6;
		transition: opacity 0.2s ease;

		&:hover {
			opacity: 1;
		}

		.el-icon {
			font-size: 12px;
		}
	}
}

// 动画定义
@keyframes rotate {
	from {
		transform: rotate(0deg);
	}
	to {
		transform: rotate(360deg);
	}
}

@keyframes uploadPulse {
	0%,
	100% {
		opacity: 0.6;
	}
	50% {
		opacity: 1;
	}
}

@keyframes successBounce {
	0% {
		transform: scale(1);
	}
	50% {
		transform: scale(1.02);
	}
	100% {
		transform: scale(1);
	}
}
</style>
